package org.dtrd.config.wechat.api.impl;

import cn.hutool.core.codec.Base64;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.dtrd.config.wechat.api.IWechatMiniprogramApi;
import org.dtrd.config.wechat.entity.bean.miniprogram.CloudBase;
import org.dtrd.config.wechat.entity.bean.miniprogram.LineColor;
import org.dtrd.config.wechat.entity.bean.miniprogram.WxaInfo;
import org.dtrd.config.wechat.entity.request.miniprogram.MiniProgramWxaCodeUnlimitRequest;
import org.dtrd.config.wechat.entity.request.miniprogram.MiniProgramQRCodeRequest;
import org.dtrd.config.wechat.entity.request.miniprogram.MiniProgramWxaCodeRequest;
import org.dtrd.config.wechat.entity.request.urllink.UrlLinkGenerateRequest;
import org.dtrd.config.wechat.entity.request.urllink.UrlLinkQueryRequest;
import org.dtrd.config.wechat.entity.request.urlscheme.UrlSchemeGenerateRequest;
import org.dtrd.config.wechat.entity.request.urlscheme.UrlSchemeQueryRequest;
import org.springframework.stereotype.Component;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.AlgorithmParameters;
import java.security.Security;
import java.util.Arrays;
import java.util.Map;

/**
 * 微信小程序
 *
 * @author xiao
 */
@Component
@Slf4j
public class WechatMiniprogramApi implements IWechatMiniprogramApi {

    /**
     * 登录凭证校验
     */
    @Override
    public JSONObject code2Session(String appId, String accessKeySecret, String jsCode) {
        Map<String, Object> map = generateBaseParams(appId, accessKeySecret);
        map.put(GRANT_TYPE, "authorization_code");
        map.put(JS_CODE, jsCode);
        String s = doGet(CODE2SESSION_PATH, map);
        return JSONObject.parseObject(s);
    }
//
//    public static void main(String[] args) {
////        accessKey: LTAI5tP8r5xUYgkydY6nHjwp
////        secretKey: 3xlnZ5V5RzcUFjwWjMw84vPV6TGUF2
////        endpoint: oss-cn-guangzhou.aliyuncs.com
////        bucketName: images-gz
//        OssBootUtil.initOSS("oss-cn-guangzhou.aliyuncs.com", "LTAI5tP8r5xUYgkydY6nHjwp", "3xlnZ5V5RzcUFjwWjMw84vPV6TGUF2");
//        OssBootUtil.setBucketName("images-gz");
//        OssBootUtil.setEndPoint("oss-cn-guangzhou.aliyuncs.com");
//        WechatMiniprogramApi wechatMiniprogramApi = new WechatMiniprogramApi();
//        AccessToken accessToken = wechatMiniprogramApi.getAccessToken("wx6d889730e9c270b0", "973664fa6b601fed2074681cec1c3ab9");
//        wechatMiniprogramApi.createQRCode(accessToken.getAccessToken(), "/pages/index/index", 300);
//        try {
//            Thread.sleep(100);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
//        LineColor color = new LineColor();
//        color.setR(123);
//        color.setB(13);
//        color.setG(23);
//        wechatMiniprogramApi.getWxacode(accessToken.getAccessToken(), "/pages/index/index", "trial", 300, false, color, true);
//    }

    /**
     * 微信小程序数解密
     *
     * @param sessionKey
     * @param encryptedData
     * @param rawData
     * @param iv
     * @param signature
     * @return
     */
    @Override
    public JSONObject decryptInfo(String sessionKey, String encryptedData, String rawData, String iv, String signature) {
        byte[] encryptArray = Base64.decode(encryptedData);
        byte[] keyByte = Base64.decode(sessionKey);
        byte[] ivByte = Base64.decode(iv);
        try {
            // 如果密钥不足16位&#xff0c;那么就补足. 这个if 中的内容很重要
            int base = 16;
            if (keyByte.length % base != 0) {
                int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
                byte[] temp = new byte[groups * base];
                Arrays.fill(temp, (byte) 0);
                System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
                keyByte = temp;
            }
            // 初始化
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
            SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
            AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
            parameters.init(new IvParameterSpec(ivByte));
            cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
            byte[] resultByte = cipher.doFinal(encryptArray);
            if (null != resultByte && resultByte.length > 0) {
                String result = new String(resultByte, "UTF-8");
                return JSONObject.parseObject(result);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 创建小程序二维码
     *
     * @param accessToken
     * @param path        小程序页面路径
     * @param width       图片宽度 280px-1280px
     * @return
     */
    @Override
    public String createQRCode(String accessToken, String path, int width) {
        MiniProgramQRCodeRequest request = new MiniProgramQRCodeRequest();
        request.setAccess_token(accessToken);
        request.setPath(path);
        if (width > 0) {
            request.setWidth(width);
        }
        log.info("WechatMiniprogramApi.createQRCode.[accessToken, path, width] " + JSONUtil.parseObj(request));
        HttpResponse httpResponse = doPostForHttpResponse(CREATEWXAQRCODE, accessToken, request);
        String url = upload(httpResponse);
        log.info("WechatMiniprogramApi.createQRCode.[accessToken, path, width] url={}", url);
        return url;
    }

    /**
     * 创建小程序码
     *
     * @param accessToken
     * @param path        页面路径
     * @param envVersion  要打开的小程序版本。正式版为 release，体验版为 trial，开发版为 develop
     * @param width       二维码宽度  最小 280px，最大 1280px
     * @param auto_color  自动配置线条颜色，如果颜色依然是黑色，则说明不建议配置主色调  默认false
     * @param line_color  auto_color 为 false 时生效，使用 rgb 设置颜色 例如 {"r":"xxx","g":"xxx","b":"xxx"} 十进制表示
     * @param is_hyaline  是否需要透明底色 默认 false
     * @return
     */
    @Override
    public String getWxacode(String accessToken, String path, String envVersion, int width, boolean auto_color, LineColor line_color, boolean is_hyaline) {
        MiniProgramWxaCodeRequest request = new MiniProgramWxaCodeRequest();
        request.setPath(path);
        if (width > 0) {
            request.setWidth(width);
        }
        if (envVersion != null) {
            request.setEnv_version(envVersion);
        }
        request.setAuto_color(auto_color);
        request.set_hyaline(is_hyaline);
        if (!auto_color && line_color != null) {
            request.setLine_color(line_color);
        }
        log.info("WechatMiniprogramApi.getWxacode.[accessToken, path, envVersion, width, auto_color, line_color, is_hyaline]" + JSONUtil.parseObj(request));
        HttpResponse httpResponse = doPostForHttpResponse(GETWXACODE, accessToken, request);
        String url = upload(httpResponse);
        log.info("WechatMiniprogramApi.getWxacode.[accessToken, path, envVersion, width, auto_color, line_color, is_hyaline] url={}", url);
        return url;
    }

    /**
     * 生成已发布的小程序码，适用于需要的码数量极多的业务场景。通过该接口生成的小程序码，永久有效，数量暂无限制。
     *
     * @param accessToken 接口调用凭证
     * @param scene
     * @param page 页面
     * @param checkPath 检查page是否存在：true为已经发布的小程序存在的页面；false时允许小程序未发布或者page不存在
     * @param envVersion 要打开的小程序的版本
     * @param width 二维码的宽度，单位 px，最小 280px，最大 1280px
     * @param autoColor 自动配置线条颜色，如果颜色依然是黑色，则说明不建议配置主色调，默认 false
     * @param lineColor auto_color 为 false 时生效，使用 rgb 设置颜色，十进制表示
     * @param isHyaline 是否需要透明底色，为 true 时，生成透明底色的小程序
     * @return
     */
    @Override
    public String getUnlimitWxacode(String accessToken, String scene, String page, boolean checkPath, String envVersion, int width, boolean autoColor, LineColor lineColor, boolean isHyaline) {
        MiniProgramWxaCodeUnlimitRequest request = new MiniProgramWxaCodeUnlimitRequest();
        request.setScene(scene);
        request.setPage(page);
        //检查 page 是否存在，为 true 时 page 必须是已经发布的小程序存在的页面(否则报错); 为 false 时允许小程序未发布或者 page 不存在， 但 page 有数量上限（60000个）请勿滥用
        if (page != null) {
            request.setCheck_path(checkPath);
        }
        if (envVersion != null) {
            request.setEnv_version(envVersion);
        }
        if (width > 0) {
            request.setWidth(width);
        }
        request.setAutu_color(autoColor);
        if (!autoColor && lineColor != null) {
            request.setLine_color(lineColor);
        }
        request.set_hyaline(isHyaline);
        log.info("WechatMiniprogramApi.getWxacodeUnlimit.[accessToken, scene, page, checkPath, envVersion, width, autoColor, lineColor, isHyaline]" + JSONUtil.parseObj(request));
        HttpResponse httpResponse = doPostForHttpResponse(GETWXACODEUNLIMIT, accessToken, request);
        String url = upload(httpResponse);
        log.info("WechatMiniprogramApi.getWxacodeUnlimit.[accessToken, scene, page, checkPath, envVersion, width, autoColor, lineColor, isHyaline] url={}", url);
        return url;
    }

    /**
     * 获取小程序 scheme 码，适用于短信、邮件、外部网页、微信内等拉起小程序的业务场景。有效期最长30天，不再永久有效
     *
     * @param accessToken     必填
     * @param jumpWxa        非必填  跳转到的目标小程序信息
     * @param expireType     非必填  到期失效的 scheme 码失效类型，失效时间：0，失效间隔天数：1
     * @param expireTime     非必填  expire_type 传值为 0，需注意 expire_time 传值的时间戳不超过 30 天，即该参数最长传值有效期为 30 天；
     * @param expireInterval 非必填  expire_type 传值为 1，需注意 expire_interval 传值范围为 [1, 30]，即该参数最长传值间隔天数为 30
     * @return
     * @see <a href="https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/url-scheme/urlscheme.generate.html">generatescheme</a>
     * @see <a href="https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/url-scheme.html">url-scheme</a>
     */
    @Override
    public String generateScheme(String accessToken, boolean isExpire, WxaInfo jumpWxa, int expireType, int expireTime, int expireInterval) {
        UrlSchemeGenerateRequest request = new UrlSchemeGenerateRequest();
        request.set_expire(isExpire);
        request.setJump_Wxa(jumpWxa);
        request.setExpire_type(expireType);
        if (!isExpire) {
            request.setExpire_time(expireTime);
            request.setExpire_interval(expireInterval);
        }
        log.info("WechatUrlSchemeApi.generateScheme.[accessToken, is_expire, jump_wxa, expire_type, expire_time, expire_interval]" + JSONUtil.parseObj(request));
        HttpResponse httpResponse = doPostForHttpResponse(GENERATESCHEME, accessToken, request);
        String url = upload(httpResponse);
        log.info("WechatUrlSchemeApi.generateScheme.[accessToken, is_expire, jump_wxa, expire_type, expire_time, expire_interval] url={}", url );
        return url;
    }

    /**
     * 查询小程序 scheme 码，及长期有效 quota
     *
     * @param accessToken
     * @param scheme      小程序 scheme 码
     * @return
     */
    @Override
    public String queryScheme(String accessToken, String scheme) {
        UrlSchemeQueryRequest request = new UrlSchemeQueryRequest();
        request.setScheme(scheme);
        log.info("WechatUrlSchemeApi.queryScheme.[accessToken, scheme]" + JSONUtil.parseObj(request));
        HttpResponse httpResponse = doPostForHttpResponse(QUERYSCHEME, accessToken, request);
        String url = upload(httpResponse);
        log.info("WechatUrlSchemeApi.queryScheme.[accessToken, scheme] url={}", url);
        return url;
    }

    /**
     * 获取小程序 URL Link，适用于短信、邮件、网页、微信内等拉起小程序的业务场景。
     *
     * @param accessToken 接口调用凭证
     * @param path 通过 URL Link 进入的小程序页面路径，必须是已经发布的小程序存在的页面，不可携带 query 。path 为空时会跳转小程序主页
     * @param query 通过 URL Link 进入小程序时的query，最大1024个字符
     * @param envVersion 要打开的小程序版本
     * @param isExpire 生成的 URL Link 类型，到期失效：true，永久有效：false
     * @param expireType 小程序 URL Link 失效类型，失效时间：0，失效间隔天数：1
     * @param expireTime 到期失效的 URL Link 的失效时间，为 Unix 时间戳
     * @param expireInterval 到期失效的URL Link的失效间隔天数
     * @param cloudBase 云开发静态网站自定义 H5 配置参数，可配置中转的云开发 H5 页面,不填默认用官方 H5 页面
     * @return
     */
    @Override
    public String generateLink(String accessToken, String path, String query, String envVersion, boolean isExpire, int expireType, int expireTime, int expireInterval, CloudBase cloudBase) {
        UrlLinkGenerateRequest request = new UrlLinkGenerateRequest();
        request.setPath(path);
        request.setQuery(query);
        if (envVersion != null) {
            request.setEnv_version(envVersion);
        }
        request.set_expire(isExpire);
        request.setExpire_type(expireType);
        //判断其是否需要填，并且还要判断它有值传进来
        if (request.getExpire_type() == 0 && expireTime > 0) {
            request.setExpire_time(expireTime);
        }
        if (request.getExpire_type() == 1 && expireInterval > 0) {
            request.setExpire_interval(expireInterval);
        }
        request.setCloud_base(cloudBase);
        log.info("WechatUrlLinkApi.generateLink.[accessToken, path, query, envVersion, isExpire, expireType, expireTime, expireInterval, cloudBase]" + JSONUtil.parseObj(request));
        HttpResponse httpResponse = doPostForHttpResponse(GENERATELINK, accessToken, request);
        String url = upload(httpResponse);
        log.info("WechatUrlLinkApi.generateLink.[accessToken, path, query, envVersion, isExpire, expireType, expireTime, expireInterval, cloudBase] url={}", url);
        return url;
    }

    /**
     * 查询小程序 url_link 配置，及长期有效 quota
     *
     * @param accessToken 接口调用凭证
     * @param urlLink 小程序url_link
     * @return
     */
    @Override
    public String queryLink(String accessToken, String urlLink) {
        UrlLinkQueryRequest request = new UrlLinkQueryRequest();
        request.setUrl_link(urlLink);
        log.info("WechatUrlLinkApi.queryLink.[accessToken, urlLink]" + JSONUtil.parseObj(request));
        HttpResponse httpResponse = doPostForHttpResponse(QUERYLINK, accessToken, request);
        String url = upload(httpResponse);
        log.info("WechatUrlLinkApi.queryLink.[accessToken, urlLink] url={}", url);
        return url;
    }

}
